package org.origin.ferret.system.service.impl;

import cn.hutool.core.util.IdUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.origin.centre.model.base.XPage;
import org.origin.centre.model.dto.XDeleteByIdDto;
import org.origin.centre.model.dto.XQueryByIdDto;
import org.origin.centre.model.vo.XPageVo;
import org.origin.ferret.system.mapper.SysClientMapper;
import org.origin.ferret.system.mapstruct.SysClientMapstruct;
import org.origin.ferret.system.model.dto.client.SysClientInsertDto;
import org.origin.ferret.system.model.dto.client.SysClientPageDto;
import org.origin.ferret.system.model.dto.client.SysClientPasswordDto;
import org.origin.ferret.system.model.dto.client.SysClientUpdateDto;
import org.origin.ferret.system.model.entity.SysClientEntity;
import org.origin.ferret.system.model.vo.client.SysClientVo;
import org.origin.ferret.system.service.SysClientService;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.time.Duration;
import java.util.Date;

import static org.origin.ferret.system.model.entity.table.SysClientEntityTableDef.SYS_CLIENT_ENTITY;

/**
 * 系统渠道-服务
 *
 * @author ferret
 * @version 2024-05-08
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class SysClientServiceImpl implements SysClientService {
    private final SysClientMapper clientMapper;
    private final SysClientMapstruct clientMapstruct;
    private final PasswordEncoder passwordEncoder;

    @Override
    public XPageVo<SysClientVo> page(XPage<SysClientPageDto> pageDto) {
        SysClientPageDto params = pageDto.getParams();
        Page<SysClientEntity> pagePojo = this.clientMapper
                .paginate(pageDto.getPageNum(), pageDto.getPageSize(), QueryWrapper.create()
                        .orderBy(SYS_CLIENT_ENTITY.ID.desc())
                );
        return new XPageVo<SysClientVo>()
                .of(pageDto, pagePojo.getTotalRow(),
                        pagePojo.getRecords(), this.clientMapstruct::toVo);
    }

    @Override
    public SysClientVo query(XQueryByIdDto queryDto) {
        SysClientEntity entity = this.clientMapper.selectOneById(queryDto.getId());
        return this.clientMapstruct.toVo(entity);
    }

    @Override
    public Boolean insert(SysClientInsertDto insertDto) {
        try {
            if (insertDto.getLiveTime() == null) insertDto.setLiveTime(3600);
            SysClientEntity entity = this.clientMapstruct.toEntity(insertDto);
            entity.setClientId(IdUtil.getSnowflakeNextIdStr());
            entity.setClientIdIssuedAt(new Date());
            TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(12)).build();
            entity.setClientSettings(this.getClientSettings());
            entity.setTokenSettings(this.getTokenSettings(insertDto.getLiveTime()));
            entity.setClientSecret(this.passwordEncoder.encode(entity.getClientSecret()));
            int insertNum = this.clientMapper.insert(entity);
            return insertNum > 0;
        } catch (DuplicateKeyException e) {
            throw new IllegalArgumentException("[" + insertDto.getClientId() + "]渠道编号已存在！");
        }
    }

    @Override
    public Boolean update(SysClientUpdateDto updateDto) {
        SysClientEntity entity = this.clientMapstruct.toEntity(updateDto);
        int updateNum = this.clientMapper.update(entity);
        entity.setClientSettings(null);
        entity.setClientSecret(null);
        entity.setTokenSettings(this.getTokenSettings(updateDto.getLiveTime()));
        return updateNum > 0;
    }

    @Override
    public Boolean delete(XDeleteByIdDto deleteDto) {
        int updateNum = this.clientMapper.deleteById(deleteDto.getId());
        return updateNum > 0;
    }

    @Override
    public Boolean password(SysClientPasswordDto passwordDto) {
        SysClientEntity clientEntity = this.clientMapper.selectOneById(passwordDto.getId());
        Assert.notNull(clientEntity, "未知的渠道信息！");
        if (!this.passwordEncoder.matches(passwordDto.getOldPassword(), clientEntity.getClientSecret())) {
            throw new IllegalArgumentException("渠道旧密码错误！");
        }
        SysClientEntity entity = new SysClientEntity();
        entity.setId(passwordDto.getId());
        entity.setClientSecret(this.passwordEncoder.encode(passwordDto.getNewPassword()));
        int updateNum = this.clientMapper.update(entity);
        return updateNum > 0;
    }

    private String getClientSettings() {
        return "{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.client.require-proof-key\":false,\"settings.client.require-authorization-consent\":true}";
    }

    private String getTokenSettings(Integer liveTime) {
        // TokenSettings build = TokenSettings.builder().accessTokenTimeToLive(Duration.ofHours(12)).build();
        return String.format("{\"@class\":\"java.util.Collections$UnmodifiableMap\",\"settings.token.reuse-refresh-tokens\":true,\"settings.token.id-token-signature-algorithm\":[\"org.springframework.security.oauth2.jose.jws.SignatureAlgorithm\",\"RS256\"],\"settings.token.access-token-time-to-live\":[\"java.time.Duration\",%d],\"settings.token.access-token-format\":{\"@class\":\"org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat\",\"value\":\"self-contained\"},\"settings.token.refresh-token-time-to-live\":[\"java.time.Duration\",%d],\"settings.token.authorization-code-time-to-live\":[\"java.time.Duration\",%d],\"settings.token.device-code-time-to-live\":[\"java.time.Duration\",%d]}", liveTime, liveTime, liveTime, liveTime);
    }

}
